(*
  Copyright (c) 2009 Barry Schwartz

  Permission is hereby granted, free of charge, to any person
  obtaining a copy of this software and associated documentation
  files (the "Software"), to deal in the Software without
  restriction, including without limitation the rights to use,
  copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the
  Software is furnished to do so, subject to the following
  conditions:

  The above copyright notice and this permission notice shall be
  included in all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  OTHER DEALINGS IN THE SOFTWARE.
*)

(*-----------------------------------------------------------------------*)

open Pycaml
open Python_tools

let py_throw exc =
  pyerr_clear ();
  raise exc

let py_fail s =
  pyerr_clear ();
  failwith s

let ff_module_ref = ref py_null

let get_ff_module () =
  if !ff_module_ref = py_null then
    let m = pyimport_importmodule "kt.fontforge" in
      if m = py_null then
        py_fail "Failed to import Python module: kt.fontforge";
      ff_module_ref := m;
      m
  else
    !ff_module_ref

let ff_load_encoding_file file_name =
  let ff = get_ff_module () in
  let args = pytuple_fromsingle (pythonize_string file_name) in
  let _ = py_module_call ff "loadEncodingFile" args in
    ()

(*-----------------------------------------------------------------------*)

module FFglyph :
sig
  type t = pyobject
  val originalgid : t -> int
  val glyph_str   : t -> string
  val glyph_enc   : t -> int
  val bbox        : t -> int * int * int * int
  val h_advance   : t -> int
  val v_advance   : t -> int
  val lsb         : t -> int
  val it_correct  : t -> int option
  val pythonize   : t -> pyobject
end =
struct
  type t = pyobject

  let originalgid g = pyint_asint (pyobject_getattrstring (g, "originalgid"))
  let glyph_str g = unpythonize_string (pyobject_getattrstring (g, "glyphname"))

  let glyph_enc g =
    let enc = pyint_asint (pyobject_getattrstring (g, "encoding")) in
      if enc = -1 then
        raise Not_found
      else
        enc (* May actually be a number beyond the nominally highest value in the encoding. *)

  let bbox g =
    let box = pytuple_toarray (py_object_call g "boundingBox" pytuple_empty) in
      (pyint_asint box.(0), pyint_asint box.(1), pyint_asint box.(2), pyint_asint box.(3))

  let h_advance g = pyint_asint (pyobject_getattrstring (g, "width"))
  let v_advance g = pyint_asint (pyobject_getattrstring (g, "vwidth"))
  let lsb g = pyint_asint (pyobject_getattrstring (g, "left_side_bearing"))

  let it_correct g =
    let n = pyint_asint (pyobject_getattrstring (g, "italicCorrection")) in
      if n = 32767 then
        None
      else
        Some n

  let pythonize g = g
end

(*-----------------------------------------------------------------------*)

type tex_font_type =
    [
    | `Unset
    | `Text
    | `Math
    | `MathExt
    ]

module FFfont :
sig
  type t
  val open_file : string -> t
  val close     : t -> unit
  val quadratic : t -> bool
  val ascent    : t -> int
  val descent   : t -> int
  val em        : t -> int
  val ps_name   : t -> string
  val glyphs    : ?encoding_order:bool -> t -> FFglyph.t array
  val str_glyph : t -> string -> FFglyph.t
  val enc_glyph : t -> int -> FFglyph.t
  val set_enc   : t -> string -> unit
  val enc       : t -> string
  val pythonize : t -> pyobject
  val unpythonize : pyobject -> t
  val tex_type  : t -> tex_font_type
  val tex_param : t -> Int64.t array
end =
struct
  type t = pyobject

  let open_file file =
    let ff = get_ff_module () in
    let f = py_module_call ff "open" (pytuple_fromsingle (pythonize_string file)) in
      if f = py_null then
        py_fail ("FontForge failed to open this font: " ^ file)
      else
        f

  let close f =
    let _ = py_object_call f "close" pytuple_empty in
      ()

  let quadratic f =
    let layers = pyobject_getattrstring (f, "layers") in
    let foreground = pymapping_getitemstring (layers, "Fore") in
      py_is_true (pyobject_getattrstring (foreground, "is_quadratic"))

  let ascent f = pyint_asint (pyobject_getattrstring (f, "ascent"))
  let descent f = pyint_asint (pyobject_getattrstring (f, "descent"))
  let em f = pyint_asint (pyobject_getattrstring (f, "em"))
  let ps_name f = unpythonize_string (pyobject_getattrstring (f, "fontname"))

  let glyphs ?(encoding_order = false) f =
    let args =
      if encoding_order then
        pytuple_fromsingle (pythonize_string "encoding")
      else
        pytuple_empty
    in
    let iter = py_object_call f "glyphs" args in
    let rec build_list p =
      let g = pyiter_next iter in
        if g = py_null then
          begin
            pyerr_clear ();
            List.rev p
          end
        else
          build_list (g :: p)
    in
      Array.of_list (build_list [])

  let str_glyph f glyphname =
    let g = pymapping_getitemstring (f, glyphname) in
      if g = py_null then
        begin
          pyerr_clear ();
          raise Not_found;
        end
      else
        g

  let enc_glyph f unicode =
    let g = pyobject_getitem (f, pyint_fromint unicode) in
      if g = py_null then
        begin
          pyerr_clear ();
          str_glyph f ".notdef";
        end
      else
        g

  let set_enc f encoding_file_name =
    let py_file_name = pythonize_string encoding_file_name in
    let error = pyobject_setattrstring (f, "encoding", py_file_name) in
      if error = (-1) then
        begin
          pyerr_clear ();
          raise Not_found (* FIX: Would some other exception be more appropriate? *)
        end

  let enc f =
    let py_enc = pyobject_getattrstring (f, "encoding") in
      unpythonize_string py_enc

  let pythonize f = f
  let unpythonize py_f = py_f

  let tex_type f =
    let kt_libff = py_module "kt.libff" in
    let args = pytuple_fromsingle (pythonize f) in
    let py_tex_type = py_module_call kt_libff "tex_font_type" args in
    let tex_type = guarded_pyint_asint py_tex_type in
      match tex_type with
        | 0 -> `Unset
        | 1 -> `Text
        | 2 -> `Math
        | 3 -> `MathExt
        | _ -> invalid_arg ("TeX font type should be between 0 and 3, but got " ^ string_of_int tex_type)

  let tex_param f =
    let kt_libff = py_module "kt.libff" in
    let args = pytuple_fromsingle (pythonize f) in
    let tex_param = py_module_call kt_libff "tex_param" args in
      unpythonize_array pyint_aslong tex_param
end

(*-----------------------------------------------------------------------*)
